package gotlib

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"math"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"time"
)

// apiString is used for type assert api for String().
type apiString interface {
	String() string
}

// apiError is used for type assert api for Error().
type apiError interface {
	Error() string
}

func IsArray(value interface{}) bool {
	rv := reflect.ValueOf(value)
	kind := rv.Kind()
	if kind == reflect.Ptr {
		rv = rv.Elem()
		kind = rv.Kind()
	}
	switch kind {
	case reflect.Array, reflect.Slice:
		return true
	default:
		return false
	}
}

func Length(any interface{}) int {
	if any == nil {
		return 0
	}

	switch value := any.(type) {
	case
		int, int8, int16, int32, int64,
		uint, uint8, uint16, uint32, uint64,
		float32, float64, bool:
		return 0
	case string:
		return len(any.(string))
	case []int:
		return len(any.([]int))
	case []int8:
		return len(any.([]int8))
	case []int16:
		return len(any.([]int16))
	case []int32:
		return len(any.([]int32))
	case []int64:
		return len(any.([]int64))
	case []uint:
		return len(any.([]uint))
	case []uint8:
		return len(any.([]uint8))
	case []uint16:
		return len(any.([]uint16))
	case []uint32:
		return len(any.([]uint32))
	case []uint64:
		return len(any.([]uint64))
	case []float32:
		return len(any.([]float32))
	case []float64:
		return len(any.([]float64))
	case []bool:
		return len(any.([]bool))
	case []string:
		return len(any.([]string))
	case []interface{}:
		return len(any.([]interface{}))
	case []time.Time:
		return len(any.([]time.Time))
	case []*time.Time:
		return len(any.([]*time.Time))
	case []map[interface{}]interface{}:
		return len(any.([]map[interface{}]interface{}))
	case []map[string]interface{}:
		return len(any.([]map[string]interface{}))
	case []map[string]string:
		return len(any.([]map[string]string))
	default:
		var (
			rv   = reflect.ValueOf(value)
			kind = rv.Kind()
		)
		switch kind {
		case reflect.Chan,
			reflect.Slice,
			reflect.Func,
			reflect.Ptr,
			reflect.Interface,
			reflect.UnsafePointer:
			if rv.IsNil() {
				return 0
			}
		case reflect.Map:
			return len(rv.MapKeys())
		case reflect.String:
			return len(rv.String())
		}
		if kind == reflect.Ptr {
			//return len(String(rv.Elem().Interface()))
			return 0
		}
		return 0
	}

}

func String(any interface{}) string {
	if any == nil {
		return ""
	}
	switch value := any.(type) {
	case int:
		return strconv.Itoa(value)
	case int8:
		return strconv.Itoa(int(value))
	case int16:
		return strconv.Itoa(int(value))
	case int32:
		return strconv.Itoa(int(value))
	case int64:
		return strconv.FormatInt(value, 10)
	case uint:
		return strconv.FormatUint(uint64(value), 10)
	case uint8:
		return strconv.FormatUint(uint64(value), 10)
	case uint16:
		return strconv.FormatUint(uint64(value), 10)
	case uint32:
		return strconv.FormatUint(uint64(value), 10)
	case uint64:
		return strconv.FormatUint(value, 10)
	case float32:
		return strconv.FormatFloat(float64(value), 'f', -1, 32)
	case float64:
		return strconv.FormatFloat(value, 'f', -1, 64)
	case bool:
		return strconv.FormatBool(value)
	case string:
		return value
	case []byte:
		return string(value)
	//case sync.Map:
	//	return SyncMapToJSON(&value)
	//case []sync.Map:
	//	tempString := "["
	//	for _, v := range value {
	//		tempString = strings.Join([]string{tempString, String(&v), ","}, "")
	//	}
	//	if len(tempString) > 1 {
	//		tempString = strings.Join([]string{tempString[0 : len(tempString)-1], "]"}, "")
	//	} else {
	//		tempString = "[]"
	//	}
	//	return tempString
	case []interface{}:
		tempString := "["
		for _, v := range value {
			tempString = strings.Join([]string{tempString, String(&v), " "}, "")
		}
		tempString = strings.Join([]string{StrRemoveTail(tempString, 2, 1), "]"}, "")
		return tempString
	case time.Time:
		if value.IsZero() {
			return ""
		}
		return value.String()
	case *time.Time:
		if value == nil {
			return ""
		}
		return value.String()
	default:
		// Empty checks.
		// if value == nil {
		// 	return ""
		// }
		if f, ok := value.(apiString); ok {
			// If the variable implements the String() interface,
			// then use that interface to perform the conversion
			return f.String()
		}
		if f, ok := value.(apiError); ok {
			// If the variable implements the Error() interface,
			// then use that interface to perform the conversion
			return f.Error()
		}
		// Reflect checks.
		var (
			rv   = reflect.ValueOf(value)
			kind = rv.Kind()
		)
		switch kind {
		case reflect.Chan,
			reflect.Map,
			reflect.Slice,
			reflect.Func,
			reflect.Ptr,
			reflect.Interface,
			reflect.UnsafePointer:
			if rv.IsNil() {
				return ""
			}
		case reflect.String:
			return rv.String()
		}
		if kind == reflect.Ptr {
			return String(rv.Elem().Interface())
		}
		// Finally, we use json.Marshal to convert.
		if jsonContent, err := json.Marshal(value); err != nil {
			return fmt.Sprint(value)
		} else {
			return string(jsonContent)
		}
	}
}

// Strings []interface{}转[]string
func Strings(any []interface{}) []string {
	if any == nil {
		return nil
	}
	var array []string
	for _, v := range any {
		array = append(array, String(v))
	}
	return array
}

// LenSyncMap 获取syncMap长度
func LenSyncMap(syncMap *sync.Map) int {
	lens := 0
	syncMap.Range(func(key, value interface{}) bool {
		lens++
		return true
	})
	return lens
}

// SyncMapToJSON SyncMap转JSON格式，todo：目前仅支持简单的转换，需要更多单元测试
func SyncMapToJSON(syncMap *sync.Map) (jsonString string) {
	jsonString = "{"
	syncMap.Range(func(key, value interface{}) bool {
		keyIsString := ""
		valueIsString := ""
		switch key.(type) {
		case string:
			keyIsString = "\""
			// break
		default:
			keyIsString = ""
			// break
		}
		switch value.(type) {
		case string:
			valueIsString = "\""
			// break
		default:
			valueIsString = ""
			// break
		}
		jsonString = strings.Join([]string{jsonString, keyIsString, String(key), keyIsString, ":", valueIsString, String(value), valueIsString, ","}, "")
		return true
	})
	jsonString = strings.Join([]string{StrRemoveTail(jsonString, 2, 1), "}"}, "")
	return
}

// StrRemoveTail 如果字符串的长度大于=最小长度，就从尾部删除removeLen长度个字符,比如最后一个为逗号时，则去掉最后一个逗号。
func StrRemoveTail(str string, minLen, removeLen int) string {
	iLen := len(str)
	if iLen >= minLen && iLen >= removeLen {
		return str[0:(iLen - removeLen)]
	} else {
		return str
	}
}

// ArrayToString ...数组转字符串
func ArrayToString(a ...interface{}) string {
	//s := fmt.Sprint(a...)
	s := ""
	for _, v := range a {
		s = strings.Join([]string{s, String(v)}, "")
	}
	return s
}

// IsNil checks whether given `value` is nil, especially for interface{} type value.
// Parameter `traceSource` is used for tracing to the source variable if given `value` is type of pinter
// that also points to a pointer. It returns nil if the source is nil when `traceSource` is true.
// Note that it might use reflect feature which affects performance a little.
func IsNil(value interface{}, traceSource ...bool) bool {
	if value == nil {
		return true
	}
	var rv reflect.Value
	if v, ok := value.(reflect.Value); ok {
		rv = v
	} else {
		rv = reflect.ValueOf(value)
	}
	switch rv.Kind() {
	case reflect.Chan,
		reflect.Map,
		reflect.Slice,
		reflect.Func,
		reflect.Interface,
		reflect.UnsafePointer:
		return !rv.IsValid() || rv.IsNil()

	case reflect.Ptr:
		if len(traceSource) > 0 && traceSource[0] {
			for rv.Kind() == reflect.Ptr {
				rv = rv.Elem()
			}
			if !rv.IsValid() {
				return true
			}
			if rv.Kind() == reflect.Ptr {
				return rv.IsNil()
			}
		} else {
			return !rv.IsValid() || rv.IsNil()
		}
	}
	return false
}

// Int64 converts `any` to int64.
func Int64(any interface{}) int64 {
	if any == nil {
		return 0
	}
	switch value := any.(type) {
	case int:
		return int64(value)
	case int8:
		return int64(value)
	case int16:
		return int64(value)
	case int32:
		return int64(value)
	case int64:
		return value
	case uint:
		return int64(value)
	case uint8:
		return int64(value)
	case uint16:
		return int64(value)
	case uint32:
		return int64(value)
	case uint64:
		return int64(value)
	case float32:
		return int64(value)
	case float64:
		return int64(value)
	case bool:
		if value {
			return 1
		}
		return 0
	case []byte:
		return int64(binary.LittleEndian.Uint64(LeFillUpSize(value, 8)))
	default:
		if _, ok := value.(int64); ok {
			return value.(int64)
		}
		var (
			s       = String(value)
			isMinus = false
		)
		if len(s) > 0 {
			if s[0] == '-' {
				isMinus = true
				s = s[1:]
			} else if s[0] == '+' {
				s = s[1:]
			}
		}
		// Hexadecimal
		if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
			if v, e := strconv.ParseInt(s[2:], 16, 64); e == nil {
				if isMinus {
					return -v
				}
				return v
			}
		}
		// Decimal
		if v, e := strconv.ParseInt(s, 10, 64); e == nil {
			if isMinus {
				return -v
			}
			return v
		}
		// Float64
		if valueInt64 := Float64(value); math.IsNaN(valueInt64) {
			return 0
		} else {
			return int64(valueInt64)
		}
	}
}

// Uint64 converts `any` to uint64.
func Uint64(any interface{}) uint64 {
	if any == nil {
		return 0
	}
	switch value := any.(type) {
	case int:
		return uint64(value)
	case int8:
		return uint64(value)
	case int16:
		return uint64(value)
	case int32:
		return uint64(value)
	case int64:
		return uint64(value)
	case uint:
		return uint64(value)
	case uint8:
		return uint64(value)
	case uint16:
		return uint64(value)
	case uint32:
		return uint64(value)
	case uint64:
		return value
	case float32:
		return uint64(value)
	case float64:
		return uint64(value)
	case bool:
		if value {
			return 1
		}
		return 0
	case []byte:
		return binary.LittleEndian.Uint64(LeFillUpSize(value, 8))
	default:
		if _, ok := value.(uint64); ok {
			return value.(uint64)
		}
		s := String(value)
		// Hexadecimal
		if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
			if v, e := strconv.ParseUint(s[2:], 16, 64); e == nil {
				return v
			}
		}
		// Decimal
		if v, e := strconv.ParseUint(s, 10, 64); e == nil {
			return v
		}
		// Float64
		if valueFloat64 := Float64(value); math.IsNaN(valueFloat64) {
			return 0
		} else {
			return uint64(valueFloat64)
		}
	}
}

// LeFillUpSize fills up the bytes `b` to given length `l` using LittleEndian.
//
// Note that it creates a new bytes slice by copying the original one to avoid changing
// the original parameter bytes.
func LeFillUpSize(b []byte, l int) []byte {
	if len(b) >= l {
		return b[:l]
	}
	c := make([]byte, l)
	copy(c, b)
	return c
}

// Float32 converts `any` to float32.
func Float32(any interface{}) float32 {
	if any == nil {
		return 0
	}
	switch value := any.(type) {
	case float32:
		return value
	case float64:
		return float32(value)
	case []byte:
		return math.Float32frombits(binary.LittleEndian.Uint32(LeFillUpSize(value, 4)))
	default:
		if _, ok := value.(float32); ok {
			return value.(float32)
		}
		v, _ := strconv.ParseFloat(String(any), 64)
		return float32(v)
	}
}

// Float64 converts `any` to float64.
func Float64(any interface{}) float64 {
	if any == nil {
		return 0
	}
	switch value := any.(type) {
	case float32:
		return float64(value)
	case float64:
		return value
	case []byte:
		return math.Float64frombits(binary.LittleEndian.Uint64(LeFillUpSize(value, 8)))
	default:
		if _, ok := value.(float64); ok {
			return value.(float64)
		}
		v, _ := strconv.ParseFloat(String(any), 64)
		return v
	}
}

func Check() {

}

// TaskTest 用于定时任务验证
func TaskTest() {
	LogD("定时任务:定时任务测试（gotlib.TaskTest）验证..................OK")
}
